/**
 * 
 */
package org.estela.ml.dataset;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;

/**
 * 
 * @version 1.00
 * @date Apr 22, 2006
 * @author <a href="mailto:akantek@nyse.com">Antonio Kantek</a>
 */
public class Instance implements Externalizable, Cloneable {
	private static final long serialVersionUID = 1L;
	
	/** */
	private double[] values;

	/** */
	private double weight = 1d;	

	/**
	 * Constructs a new <code>Instance</code>. 
	 */
	public Instance() {
	}
	
	/**
	 * Constructs a new <code>Instance</code>. 
	 * @param values
	 */
	public Instance(double[] values) {
		this(values, 1d);
	}

	/**
	 * Constructs a new <code>Instance</code>. 
	 * @param values
	 * @param weight
	 */
	public Instance(double[] values, double weight) {
		this.values = values;
		this.weight = weight;
	}
	
	@Override
	public String toString() {
		StringBuilder buff = new StringBuilder("[Instance weight=");
		buff.append(this.weight).append(", values=");
		buff.append(Arrays.toString(this.values));
		buff.append(']');
		return buff.toString();
	}
	
	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Instance) {
			Instance other = (Instance) obj;
			return this.weight == other.weight &&
				Arrays.equals(this.values, other.values);
		}
		return false;
	}
	
	@Override
	public int hashCode() {
        long bits = Double.doubleToLongBits(this.weight);
        int hash = 7 * (int)(bits ^ (bits >>> 32));
        hash *= 37 * Arrays.hashCode(this.values); 
		return hash; 
	}
	
	/*
	 *  (non-Javadoc)
	 * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
	 */
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeDouble(this.weight);
		out.writeInt(this.values.length);
		for (int i = 0; i < this.values.length; i++) {
			out.writeDouble(this.values[i]);
		}
	}

	/*
	 *  (non-Javadoc)
	 * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
	 */
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		this.weight = in.readDouble();
		this.values = new double[in.readInt()];
		for (int i = 0; i < this.values.length; i++) {
			this.values[i] = in.readDouble();
		}
	}	

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Instance instance = new Instance();
		instance.setValues(new double[]{1d,2d,3d});
		System.out.println(instance);
		
		Instance clone = instance.clone();
		System.out.println(instance.equals(clone));
		System.out.println(instance == clone);
		
		
		
	}

	/**
	 * @return Returns the values.
	 */
	public double[] getValues() {
		return values;
	}

	/**
	 * @param values The values to set.
	 */
	public void setValues(double[] values) {
		this.values = values;
	}
	
	/**
	 * 
	 * @param idx
	 * @param value
	 */
	public void setValueAt(int idx, double value) {
		this.values[idx] = value;
	}
	
	public double getValueAt(int idx) {
		return this.values[idx];
	}
	
	 @Override
	public Instance clone() {
		Instance clone = null;
		try {
			clone = (Instance) super.clone();
			clone.values = this.values.clone();
		} catch (CloneNotSupportedException e) {
			throw new Error(e);
		}
		return clone;
	}

	/**
	 * @return Returns the weight.
	 */
	public double getWeight() {
		return weight;
	}

	/**
	 * @param weight The weight to set.
	 */
	public void setWeight(double weight) {
		this.weight = weight;
	}

	public void setMissing(int index) {
		this.values[index] = Double.NaN;
		
	}
}
